﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;

namespace Server
{
    /// <summary>
    /// 从 TCP 网络客户端侦听连接。
    /// </summary>
    public class TcpServer
    {
        private bool isClosing = false; // 服务器 closing 标志位
        private TcpListener server; // 服务器的 TcpListener
        private Action<TcpConnection> connectinAction; // 每个客户端连接的委托

        /// <summary>
        /// 服务器运行状态
        /// </summary>
        public bool IsRunning { get; private set; }

        /// <summary>
        /// 使用指定的本地终结点初始化 TcpServer 类的新实例。
        /// </summary>
        /// <param name="localEP">将 TcpServer 绑定到的本地终结点。</param>
        public TcpServer(IPEndPoint localEP)
        {
            IsRunning = false;
            server = new TcpListener(localEP);
        }

        /// <summary>
        /// 初始化 TcpServer 类的新实例，该类在指定的本地 IP 地址和端口号上侦听是否有传入的连接尝试。
        /// </summary>
        /// <param name="localaddr">本地 IP 地址</param>
        /// <param name="port">用来侦听传入的连接尝试的端口。</param>
        public TcpServer(IPAddress localaddr, int port) : this(new IPEndPoint(localaddr, port))
        {
        }

        /// <summary>
        /// 初始化 TcpServer 类的新实例，该类在指定的本地 IP 地址和端口号上侦听是否有传入的连接尝试。
        /// </summary>
        /// <param name="address">本地 IP 地址字符串</param>
        /// <param name="port">用来侦听传入的连接尝试的端口。</param>
        public TcpServer(string address, int port) : this(IPAddress.Parse(address), port)
        {
        }

        /// <summary>
        /// 开始侦听传入的连接请求。
        /// </summary>
        /// <param name="action">每个客户端连接对应的委托</param>
        public void Start(Action<TcpConnection> action)
        {
            if (!IsRunning)
            {
                IsRunning = true;
                isClosing = false;
                connectinAction = action;
                server.Start();
                StartAccept();
            }
        }

        /// <summary>
        /// 获取当前实例的基础 EndPoint。
        /// </summary>
        public EndPoint LocalEndPoint
        {
            get { return server.LocalEndpoint; }
        }

        /// <summary>
        /// 获取或设置一个 bool 值，该值指定该实例是否只允许一个基础套接字来侦听特定端口。
        /// </summary>
        public bool ExclusiveAddressUse
        {
            get { return server.ExclusiveAddressUse; }
            set { server.ExclusiveAddressUse = value; }
        }

        /// <summary>
        /// 获取基础网络 Socket
        /// </summary>
        public Socket Server
        {
            get { return server.Server; }
        }

        /// <summary>
        /// 关闭侦听器。
        /// </summary>
        public void Stop()
        {
            isClosing = true;
            server.Stop();
            IsRunning = false;
        }

        /// <summary>
        /// 开始一个异步操作来接受一个传入的连接尝试。
        /// </summary>
        private void StartAccept()
        {
            server.BeginAcceptTcpClient(iar =>
            {
                try
                {
                    if (isClosing) return;
                    TcpClient tcpClient = server.EndAcceptTcpClient(iar);
                    TcpConnection client = new TcpConnection(tcpClient, connectinAction);
                }
                catch (Exception ex)
                {
                    Trace.TraceError(ex.Message);
                }
                StartAccept();
            }, null);

        }

        /// <summary>
        /// 返回指定终结点的 IP 地址和端口号。
        /// </summary>
        /// <returns>包含指定终结点（例如，192.168.1.2:80）的 IP 地址和端口号的字符串。</returns>
        public override string ToString()
        {
            return LocalEndPoint.ToString();
        }

        /// <summary>
        /// 获取本地IP地址
        /// </summary>
        /// <param name="addressFamily">地址族,默认IPv4</param>
        /// <returns>本地IP地址数组</returns>
        public static IPAddress[] GetLocalAddress(AddressFamily addressFamily = AddressFamily.InterNetwork)
        {
            List<IPAddress> list = new List<IPAddress>();
            foreach (var item in Dns.GetHostAddresses(Dns.GetHostName()))
            {
                if (item.AddressFamily == addressFamily)
                    list.Add(item);
            }
            return list.ToArray();
        }

        /// <summary>
        /// 获取正在使用中的端口
        /// </summary>
        /// <param name="address">指定IP地址,默认全部地址</param>
        /// <returns>正在使用中的端口数组</returns>
        public static int[] GetInUsedPort(string address = null)
        {
            List<IPEndPoint> localEP = new List<IPEndPoint>();
            List<int> localPort = new List<int>();
            IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
            localEP.AddRange(ipGlobalProperties.GetActiveTcpListeners());
            localEP.AddRange(ipGlobalProperties.GetActiveUdpListeners());
            localEP.AddRange(ipGlobalProperties.GetActiveTcpConnections().Select(item => item.LocalEndPoint));
            foreach (var item in localEP.Distinct())
            {
                if (address == null || item.Address.ToString() == address)
                    localPort.Add(item.Port);
            }
            localPort.Sort();
            return localPort.Distinct().ToArray();
        }

        /// <summary>
        /// 随机获取一个大于等于 min 的空闲端口
        /// </summary>
        /// <param name="min">指定起始端口，默认1024</param>
        /// <param name="address">指定IP地址,默认全部地址</param>
        /// <returns>一个指定IP地址与范围的随机空闲端口</returns>
        public static int GetFreePort(int min = 1024, string address = null)
        {
            int freePort = -1;
            Random random = new Random();
            int[] freePorts = GetInUsedPort(address).Where(x => x >= min).ToArray();
            while (freePort < 0)
            {
                freePort = random.Next(min, 65536);
                foreach (var item in freePorts)
                {
                    if (freePort == item)
                        freePort = -1;
                }
            }
            return freePort;
        }
    }

    /// <summary>
    /// TCP 网络服务的一个客户端连接。
    /// </summary>
    public class TcpConnection : IDisposable
    {
        private bool isClosing = false; // 当前连接 closing 标志位
        private byte[] buffer; // 当前连接用于 receive 的数据缓冲区
        private TcpClient tcpClient; // 当前连接的 TcpClient 对象
        private NetworkStream networkStream; // 当前连接的 NetworkerStream 对象
        private readonly Action<TcpConnection> initialize; // 当前连接的委托

        /// <summary>
        /// 通过指定的 TcpClient 和 Action 实例化一个与服务器建立连接的客户端。
        /// </summary>
        /// <param name="client">指定的 TcpClient。</param>
        /// <param name="action">客户端委托。</param>
        public TcpConnection(TcpClient client, Action<TcpConnection> action)
        {
            tcpClient = client;
            initialize = action;
            networkStream = tcpClient.GetStream();
            RemoteEndPoint = CopyEndPoint(tcpClient.Client.RemoteEndPoint);
            OnAccept = x => { };
            OnReceive = (x, y) => { };
            OnClose = (x, y) => { };
            OnError = (x, y) => { };
            initialize(this);
            OnAccept(this);
            buffer = new byte[tcpClient.ReceiveBufferSize];
            StartReceive();
        }

        /// <summary>
        /// 获取 EndPoint 的深拷贝
        /// </summary>
        /// <param name="endPoint">需要拷贝的源对象</param>
        /// <returns>目标副本</returns>
        private EndPoint CopyEndPoint(EndPoint endPoint)
        {
            return endPoint.Create(endPoint.Serialize());
        }

        /// <summary>
        /// 从当前连接开始异步读取。
        /// </summary>
        private void StartReceive()
        {
            try
            {
                networkStream.BeginRead(buffer, 0, buffer.Length, iar =>
                {
                    try
                    {
                        if (isClosing) return;
                        int size = networkStream.EndRead(iar);
                        if (size == 0 || !tcpClient.Connected || !networkStream.CanRead)
                        {
                            Close(true);
                            return;
                        }
                        byte[] data = new byte[size];
                        Buffer.BlockCopy(buffer, 0, data, 0, size);
                        OnReceive(this, data);
                    }
                    catch (Exception ex)
                    {
                        OnError(this, ex);
                    }
                    StartReceive();
                }, null);
            }
            catch (Exception ex)
            {
                OnError(this, ex);
            }
        }

        /// <summary>
        /// 获取或设置一个值，该值在发送或接收缓冲区未满时禁用延迟。
        /// </summary>
        public bool NoDelay
        {
            get { return tcpClient.NoDelay; }
            set { tcpClient.NoDelay = value; }
        }

        /// <summary>
        /// 获取已经从网络接收且可供读取的数据量。
        /// </summary>
        public int Available
        {
            get { return tcpClient.Available; }
        }

        /// <summary>
        /// 获取或设置基础 Socket。
        /// </summary>
        public Socket Client
        {
            get { return tcpClient.Client; }
        }

        /// <summary>
        /// 获取当前连接的终节点
        /// </summary>
        public EndPoint RemoteEndPoint
        {
            get; private set;
        }

        /// <summary>
        /// 获取一个 bool 值，该值指示 Socket 是否已连接到远程主机。
        /// </summary>
        public bool Connected
        {
            get { return tcpClient.Connected; }
        }

        /// <summary>
        /// 获取或设置 bool 值，该值指定是否只允许一个客户端使用端口。
        /// </summary>
        public bool ExclusiveAddressUse
        {
            get { return tcpClient.ExclusiveAddressUse; }
            set { tcpClient.ExclusiveAddressUse = value; }
        }

        /// <summary>
        /// 开始向当前连接异步写入。
        /// </summary>
        /// <param name="data">类型 Byte 的数组，该数组包含要写入的数据。</param>
        /// <returns></returns>
        public IAsyncResult Send(byte[] data)
        {
            return networkStream.BeginWrite(data, 0, data.Length, iar =>
            {
                try
                {
                    if (!tcpClient.Connected || !networkStream.CanRead)
                    {
                        Close(true);
                        return;
                    }
                    networkStream.EndWrite(iar);
                }
                catch (Exception ex)
                {
                    OnError(this, ex);
                }
            }, null);
        }

        /// <summary>
        /// 开始向当前连接异步写入。
        /// </summary>
        /// <param name="message">该 string 包含要写入的数据。</param>
        /// <returns></returns>
        public IAsyncResult Send(string message)
        {
            return Send(Encoding.UTF8.GetBytes(message));
        }

        /// <summary>
        /// 关闭基础连接并释放所有资源
        /// </summary>
        /// <param name="activeExit">是否主动关闭</param>
        private void Close(bool activeExit)
        {
            isClosing = true;
            networkStream.Close();
            tcpClient.Close();
            OnClose(this, activeExit);
        }

        /// <summary>
        /// 关闭基础连接并释放所有资源
        /// </summary>
        public void Close()
        {
            Close(false);
        }

        /// <summary>
        /// 关闭基础连接并释放所有资源,和Close()效果相同
        /// </summary>
        public void Dispose()
        {
            Close(false);
        }

        /// <summary>
        /// 返回指定终结点的 IP 地址和端口号。
        /// </summary>
        /// <returns>包含指定终结点（例如，192.168.1.2:80）的 IP 地址和端口号的字符串。</returns>
        public override string ToString()
        {
            return RemoteEndPoint.ToString();
        }

        /// <summary>
        /// TcpConnection 的 accept 事件
        /// </summary>
        public Action<TcpConnection> OnAccept { get; set; }

        /// <summary>
        /// TcpConnection 的 receive 事件
        /// </summary>
        public Action<TcpConnection, byte[]> OnReceive { get; set; }

        /// <summary>
        /// TcpConnection 的 close 事件
        /// </summary>
        public Action<TcpConnection, bool> OnClose { get; set; }

        /// <summary>
        /// TcpConnection 的 error 事件
        /// </summary>
        public Action<TcpConnection, Exception> OnError { get; set; }

    }
}
